#include "AffixFilter.h"
#include "Globals.h"

#include "MagicItemGenerator.h"

#include "Components/EventManager.h"

#include "API/TES/TESDataHandler.h"
#include "API/TESFiles/TESFile.h"
#include "API/CSDialogs/TESDialog.h"
#include "API/BSTypes/BSStringT.h"
#include "Utilities/Memaddr.h"

#include "Utilities.h"

#ifdef OBLIVION
	#include "API/Actors/ActorValues.h"
	#include "API/Items/TESObjectARMO.h"
	#include "API/Items/TESObjectCLOT.h"
	#include "API/Items/TESObjectWEAP.h"
	#include "Submodule/AffixList.h"

	// RegEx
	#include <boost/regex.hpp>
#else //ifndef OBLIVION
	#include "ARES.rc.h"
	#include "AffixFilterDlg.h"
	#include "FilterTabDlg.h"
	#include "CSVparser.h"
#endif

#pragma warning(disable : 4996) // sprintf is deprecated

// local declaration of module handle defined in submodule.cpp
// this handle is needed to extract resources embedded in this module (e.g. dialog templates)
extern HMODULE hModule;

AffixFilter::~AffixFilter() {
    _VMESSAGE("Destroying '%s'/%p:%p @ &lt;%p&gt;",GetEditorID(),GetFormType(),formID,this);
	// don't really need to clean anything up.
}

bool AffixFilter::LoadForm(TESFile& file)
{
	_VMESSAGE("Loading '%s'/%p:%p @ &lt;%p&gt;",GetEditorID(),GetFormType(),formID,this);
	/*
		Load form data from a file record.
		This method must be overwritten (TESForm::LoadForm does nothing), unless this
		form class derives from some child of TESForm that has already overwritten it.

		Chunks are usually saved in a fixed order, but all vanilla implementations of LoadForm
		allow them to be loaded in a more flexible order - hence the loop and switch over chunk
		types.
	*/

	file.InitializeFormFromRecord(*this); // initialize formID, formFlags, etc. from record header

	char buffer[0x200];
	for(UInt32 chunktype = file.GetChunkType(); chunktype; chunktype = file.GetNextChunk() ? file.GetChunkType() : 0) {
		// process chunk
		switch (chunktype) {
			// editor id      
			case 'DIDE':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1); // load chunk into buffer
				_VMESSAGE("EDID chunk: '%s'",buffer);
				SetEditorID(buffer);
				break;

			case 'NAME':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetNameFilter(buffer);
				_VMESSAGE("NAME chunk: '%s'", nameFilter.c_str());
				break;

			case 'minV':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minVal));
				SetMinValue(*(UInt8*)buffer);
				_VMESSAGE("minV chunk: %i", minVal);
				break;

			case 'maxV':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxVal));
				SetMaxValue(*(SInt16*)buffer);
				_VMESSAGE("maxV chunk: %i", maxVal);
				break;

			case 'minW':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minWht));
				SetMinWeight(*(UInt8*)buffer);
				_VMESSAGE("minW chunk: %i", minWht);
				break;

			case 'maxW':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxWht));
				SetMaxWeight(*(SInt16*)buffer);
				_VMESSAGE("maxW chunk: %i", maxWht);
				break;

			case 'AMSK':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(app_types));
				SetApparelMask(*(UInt8*)buffer);
				_VMESSAGE("ATYP chunk: %i", app_types);
				break;

			case 'WMSK':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(wep_types));
				SetWeaponMask(*(UInt8*)buffer);
				_VMESSAGE("WTYP chunk: %i", wep_types);

				break;

			case 'minH':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minDur));
				SetMinDurability(*(UInt8*)buffer);
				_VMESSAGE("minH chunk: %i", minDur);
				break;

			case 'maxH':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxDur));
				SetMaxDurability(*(SInt16*)buffer);
				_VMESSAGE("maxH chunk: %i", maxDur);

				break;

			case 'minD':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minDmg));
				SetMinDamage(*(UInt16*)buffer);
				_VMESSAGE("minD chunk: %i", minDmg);
				break;

			case 'maxD':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxDmg));
				SetMaxDamage(*(UInt16*)buffer);
				_VMESSAGE("maxD chunk: %i", maxDmg);
				break;

			case 'minR':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minRng));
				SetMinReach(*(UInt8*)buffer);
				_VMESSAGE("minR chunk: %i", minRng);
				break;

			case 'maxR':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxRng));
				SetMaxReach(*(SInt16*)buffer);
				_VMESSAGE("maxR chunk: %i", maxRng);
				break;

			case 'minS':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(minSpd));
				SetMinSpeed(*(UInt8*)buffer);
				_VMESSAGE("minS chunk: %i", minSpd);
				break;

			case 'maxS':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(maxSpd));
				SetMaxSpeed(*(SInt16*)buffer);
				_VMESSAGE("maxS chunk: %i", maxSpd);
				break;

			case 'iNWR':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(ignoresNWRes));
				SetIgnoresNormalWeaponResistance(*(tri*)buffer);
				_VMESSAGE("iNWR chunk: %i", ignoresNWRes);
				break;

			case 'MESH':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetModelFilterWeapon(buffer);
				_VMESSAGE("MESH chunk: '%s'", weaponFilter.c_str());
				break;

			case 'ICON':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetIconFilterWeapon(buffer);
				_VMESSAGE("ICON chunk: '%s'", iconWeaponFilter.c_str());
				break;

			case 'hidR':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(hidesRings));
				SetHidesRings(*(tri*)buffer);
				_VMESSAGE("hidR chunk: %i", hidesRings);
				break;

			case 'hidA':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(hidesAmulet));
				SetHidesAmulet(*(tri*)buffer);
				_VMESSAGE("hidA chunk: %i", hidesAmulet);
				break;

			case 'head':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(head));
				SetBipedHead(*(tri*)buffer);
				_VMESSAGE("head chunk: %i", head);
				break;

			case 'hair':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(hair));
				SetBipedHair(*(tri*)buffer);
				_VMESSAGE("hair chunk: %i", hair);
				break;

			case 'bodU':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(bodyUp));
				SetBipedBodyUpper(*(tri*)buffer);
				_VMESSAGE("bodU chunk: %i", bodyUp);
				break;

			case 'bodL':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(bodyLo));
				SetBipedBodyLower(*(tri*)buffer);
				_VMESSAGE("bodL chunk: %i", bodyLo);
				break;

			case 'hand':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(hand));
				SetBipedHand(*(tri*)buffer);
				_VMESSAGE("hand chunk: %i", hand);
				break;

			case 'foot':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(foot));
				SetBipedFoot(*(tri*)buffer);
				_VMESSAGE("foot chunk: %i", foot);
				break;

			case 'rngR':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(ringR));
				SetBipedRingRight(*(tri*)buffer);
				_VMESSAGE("rngR chunk: %i", ringR);
				break;

			case 'rngL':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(ringL));
				SetBipedRingLeft(*(tri*)buffer);
				_VMESSAGE("rngL chunk: %i", ringL);
				break;

			case 'ammy':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(amulet));
				SetBipedAmulet(*(tri*)buffer);
				_VMESSAGE("ammy chunk: %i", amulet);
				break;

			case 'wep ':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(weapon));
				SetBipedWeapon(*(tri*)buffer);
				_VMESSAGE("wep  chunk: %i", weapon);
				break;

			case 'wepB':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(weaponBack));
				SetBipedWeaponBack(*(tri*)buffer);
				_VMESSAGE("wepB chunk: %i", weaponBack);
				break;

			case 'wepS':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(tri));
				SetBipedWeaponSide(*(tri*)buffer);
				_VMESSAGE("wepS chunk: %i", weaponSide);
				break;

			case 'quiv':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(quiver));
				SetBipedQuiver(*(tri*)buffer);
				_VMESSAGE("quiv chunk: %i", quiver);
				break;

			case 'shld':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(shield));
				SetBipedShield(*(tri*)buffer);
				_VMESSAGE("shld chunk: %i", shield);
				break;

			case 'trch':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(torch));
				SetBipedTorch(*(tri*)buffer);
				_VMESSAGE("trch chunk: %i", torch);
				break;

			case 'tail':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(tail));
				SetBipedTail(*(tri*)buffer);
				_VMESSAGE("tail chunk: %i", tail);
				break;

			case 'mask':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(numMask));
				numMask = *(UInt16*)buffer;
				_VMESSAGE("mask chunk: %i", numMask);
				break;

			case 'MBMF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetModelFilterBipedMale(buffer);
				_VMESSAGE("MBMF chunk: '%s'", bipedMaleFilter.c_str());
				break;

			case 'MBFF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetModelFilterBipedFemale(buffer);
				_VMESSAGE("MBFF chunk: '%s'", bipedFemaleFilter.c_str());
				break;

			case 'MGMF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetModelFilterGroundMale(buffer);
				_VMESSAGE("MGMF chunk: '%s'", groundMaleFilter.c_str());
				break;

			case 'MGFF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetModelFilterGroundFemale(buffer);
				_VMESSAGE("MGFF chunk: '%s'", groundFemaleFilter.c_str());
				break;

			case 'ICMF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetIconFilterMale(buffer);
				_VMESSAGE("ICMF chunk: '%s'", iconMaleFilter.c_str());
				break;

			case 'ICFF':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(buffer)-1);
				SetIconFilterFemale(buffer);
				_VMESSAGE("ICFF chunk: '%s'", iconFemaleFilter.c_str());
				break;

			case 'MIG ':
				memset(buffer,0,sizeof(buffer));
				{
					Affixes::AffixesSaveData loadChunk;
					file.GetChunkData(buffer,sizeof(loadChunk));
					loadChunk = *(Affixes::AffixesSaveData*)buffer;
					if ( TESForm::ResolveFormID(loadChunk.formID, file) ) {
						MagicItemGenerator* mig = dynamic_cast<MagicItemGenerator*>(TESForm::LookupByFormID(loadChunk.formID));
						if ( mig ) {
							migs.AddTail(new Affixes(mig, loadChunk.souls));
							_VMESSAGE("MIG  chunk: '%s'", mig->GetEditorID());
						}
						else
							_ERROR("MIG  chunk: bad pointer?");
					}
					else
						_WARNING("MIG  chunk: MagicItemGenerator's mod is not loaded.");
				}
				break;

			case 'rar1':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer, sizeof(buffer));
				AddRareFirstName(buffer);
				_VMESSAGE("rar1 chunk: '%s'", rareFirsts.GetTail()->c_str());
				break;

			case 'rar2':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer, sizeof(buffer));
				AddRareSecondName(buffer);
				_VMESSAGE("rar2 chunk: '%s'", rareSeconds.GetTail()->c_str());
				break;

/*			case 'soul':
				memset(buffer,0,sizeof(buffer));
				file.GetChunkData(buffer,sizeof(UInt8));
				if ( migs.GetTail() ) {
					migs.GetTail()->second = *(UInt8*)buffer;
					_VMESSAGE("soul chunk: %01X", migs.GetTail()->second);
				}
				else
					_VMESSAGE("soul chunk: no MIG?");
				break;
*/
			default: {
				gLog.PushStyle();
				UInt32 swapchunk = Swap32(chunktype);
				_WARNING("Unexpected chunk '%4.4s' {%08X} w/ size %08X", &swapchunk, chunktype, file.currentChunk.chunkLength);
				gLog.PopStyle();
				break;
			}
		}
		// continue to next chunk
	}
	return true;
}

void AffixFilter::SaveFormChunks() {
    _VMESSAGE("Saving '%s'/%p:%p @ &lt;%p&gt;",GetEditorID(),GetFormType(),formID,this);
    /*
        Save form data to a file record.
        This method must be overwritten (TESForm::SaveFormChunks does nothing), unless this
        form class derives from some child of TESForm that has already overwritten it.
    */

    // initialize the global Form Record memory buffer, to which chunks are written by all 'Save' methods
    // InitializeFormRecord() also automatically saves the EDID chunk
    InitializeFormRecord();

	PutFormRecordChunkData('NAME', (void*)nameFilter.c_str(), nameFilter.Size());
	PutFormRecordChunkData('minV', &minVal, sizeof(minVal));
	PutFormRecordChunkData('maxV', &maxVal, sizeof(maxVal));
	PutFormRecordChunkData('minW', &minWht, sizeof(minWht));
	PutFormRecordChunkData('maxW', &maxWht, sizeof(maxWht));
	PutFormRecordChunkData('AMSK', &app_types, sizeof(app_types));
	PutFormRecordChunkData('WMSK', &wep_types, sizeof(wep_types));

	PutFormRecordChunkData('minH', &minDur, sizeof(minDur));
	PutFormRecordChunkData('maxH', &maxDur, sizeof(maxDur));

	PutFormRecordChunkData('minD', &minDmg, sizeof(minDmg));
	PutFormRecordChunkData('maxD', &maxDmg, sizeof(maxDmg));
	PutFormRecordChunkData('minR', &minRng, sizeof(minRng));
	PutFormRecordChunkData('maxR', &maxRng, sizeof(maxRng));
	PutFormRecordChunkData('minS', &minSpd, sizeof(minSpd));
	PutFormRecordChunkData('maxS', &maxSpd, sizeof(maxSpd));
	PutFormRecordChunkData('iNWR', &ignoresNWRes, sizeof(ignoresNWRes));
	if ( weaponFilter.Size() > 0 )
		PutFormRecordChunkData('MESH', (void*)weaponFilter.c_str(), weaponFilter.Size());
	if ( iconWeaponFilter.Size() > 0 )
		PutFormRecordChunkData('ICON', (void*)iconWeaponFilter.c_str(), iconWeaponFilter.Size());

	PutFormRecordChunkData('hidR', &hidesRings, sizeof(hidesRings));
	PutFormRecordChunkData('hidA', &hidesAmulet, sizeof(hidesAmulet));
	PutFormRecordChunkData('head', &head, sizeof(head));
	PutFormRecordChunkData('hair', &hair, sizeof(hair));
	PutFormRecordChunkData('bodU', &bodyUp, sizeof(bodyUp));
	PutFormRecordChunkData('bodL', &bodyLo, sizeof(bodyLo));
	PutFormRecordChunkData('hand', &hand, sizeof(hand));
	PutFormRecordChunkData('foot', &foot, sizeof(foot));
	PutFormRecordChunkData('rngR', &ringR, sizeof(ringR));
	PutFormRecordChunkData('rngL', &ringL, sizeof(ringL));
	PutFormRecordChunkData('ammy', &amulet, sizeof(amulet));
	PutFormRecordChunkData('wep ', &weapon, sizeof(weapon));
	PutFormRecordChunkData('wepB', &weaponBack, sizeof(weaponBack));
	PutFormRecordChunkData('wepS', &weaponSide, sizeof(weaponSide));
	PutFormRecordChunkData('quiv', &quiver, sizeof(quiver));
	PutFormRecordChunkData('shld', &shield, sizeof(shield));
	PutFormRecordChunkData('trch', &torch, sizeof(torch));
	PutFormRecordChunkData('tail', &tail, sizeof(tail));
	if ( bipedMaleFilter.Size() > 0 )
		PutFormRecordChunkData('MBMF', (void*)bipedMaleFilter.c_str(), bipedMaleFilter.Size());
	if ( bipedFemaleFilter.Size() > 0 )
		PutFormRecordChunkData('MBFF', (void*)bipedFemaleFilter.c_str(), bipedFemaleFilter.Size());
	if ( groundMaleFilter.Size() > 0 )
		PutFormRecordChunkData('MGMF', (void*)groundMaleFilter.c_str(), groundMaleFilter.Size());
	if ( groundFemaleFilter.Size() > 0 )
		PutFormRecordChunkData('MGFF', (void*)groundFemaleFilter.c_str(), groundFemaleFilter.Size());
	if ( iconMaleFilter.Size() > 0 )
		PutFormRecordChunkData('ICMF', (void*)iconMaleFilter.c_str(), iconMaleFilter.Size());
	if ( iconFemaleFilter.Size() > 0 )
		PutFormRecordChunkData('ICFF', (void*)iconFemaleFilter.c_str(), iconFemaleFilter.Size());

	PutFormRecordChunkData('mask', &numMask, sizeof(numMask));

	NiTListIterator i = migs.GetHeadPos();
	while ( i ) {
		Affixes* affPtr = migs.GetNext(i);
		Affixes::AffixesSaveData saveChunk = { affPtr->first->formID, affPtr->second };
		void* chunk = (void*)&saveChunk;
		PutFormRecordChunkData('MIG ', chunk, sizeof(saveChunk));
		saveChunk = *(Affixes::AffixesSaveData*)chunk;
		if ( saveChunk.formID != affPtr->first->formID )
			_ERROR("MIG #%i form ID saved as &lt;%p&gt;, should be &lt;%p&gt;.", i, saveChunk.formID, affPtr->first->formID);
		if ( saveChunk.souls != affPtr->second )
			_ERROR("MIG #%i souls saved as %01X, should %01X.", i, saveChunk.souls, affPtr->second);
	}

	if ( rareFirsts.GetSize() > 0 ) {
		BSStringT* strptr = NULL;
		i = rareFirsts.GetHeadPos();
		while (i) {
			strptr = rareFirsts.GetNext(i);
			if (strptr)
				PutFormRecordChunkData('rar1', (void*)strptr->c_str(), strptr->Size());
		}
	}

	if ( rareSeconds.GetSize() > 0 ) {
		BSStringT* strptr = NULL;
		i = rareSeconds.GetHeadPos();
		while (i) {
			strptr = rareSeconds.GetNext(i);
			if (strptr)
				PutFormRecordChunkData('rar2', (void*)strptr->c_str(), strptr->Size());
		}
	}

	// close the global form record buffer & write out to disk
	FinalizeFormRecord();
}

UInt8 AffixFilter::GetFormType()
{
    /*
        Return the form type code.  This method *MUST* be overwritten
        It must return the type code assigned when this form type was registered with ExtendedForm.
        It may, in exceptional circumstances, also return zero as an 'invalid type' code.
    */
    return extendedForm.FormType();
}

void AffixFilter::CopyFrom(TESForm& form)
{
    _VMESSAGE("Copying '%s'/%p:%p @ &lt;%p&gt; ONTO '%s'/%p:%p @ &lt;%p&gt; ",
        form.GetEditorID(),form.GetFormType(),form.formID,&form,GetEditorID(),GetFormType(),formID,this);
    /*
        Copy member values.  This method *MUST* be overwritten (TESForm::CopyFrom does nothing).
        This function does not copy 'index' values like formID, editorID, mgefCode, etc. *unless*
        one of the forms involved is flagged as temporary.
    */

    AffixFilter* source = dynamic_cast<AffixFilter*>(&form);
    if (!source) return;    // source has wrong polymorphic type

    CopyAllComponentsFrom(form); // copy all BaseFormComponent properties
	migs.RemoveAll();
	if ( source->migs.GetSize() > 0 ) {
		NiTListIterator i = source->migs.GetHeadPos();
		while (i) {
			migs.AddTail(source->migs.GetNext(i));
		}
	}
	nameFilter = source->nameFilter;
	minVal = source->minVal;
	maxVal = source->maxVal;
	minWht = source->minWht;
	maxWht = source->maxWht;
	app = source->app;
	wep = source->wep;
	app_types = source->app_types;
	wep_types = source->wep_types;
	minDur = source->minDur;
	maxDur = source->maxDur;

	minDmg = source->minDmg;
	maxDmg = source->maxDmg;
	minRng = source->minRng;
	maxRng = source->maxRng;
	minSpd = source->minSpd;
	maxSpd = source->maxSpd;
	ignoresNWRes = source->ignoresNWRes;
	weaponFilter = source->weaponFilter;
	iconWeaponFilter = source->iconWeaponFilter;

	hidesRings = source->hidesRings;
	hidesAmulet = source->hidesAmulet;
	head = source->head;
	hair = source->hair;
	bodyUp = source->bodyUp;
	bodyLo = source->bodyLo;
	hand = source->hand;
	foot = source->foot;
	ringR = source->ringR;
	ringL = source->ringL;
	amulet = source->amulet;
	weapon = source->weapon;
	weaponBack = source->weaponBack;
	weaponSide = source->weaponSide;
	quiver = source->quiver;
	shield = source->shield;
	torch = source->torch;
	tail = source->tail;
	bipedMaleFilter = source->bipedMaleFilter;
	bipedFemaleFilter = source->bipedFemaleFilter;
	groundMaleFilter = source->groundMaleFilter;
	groundFemaleFilter = source->groundFemaleFilter;
	iconMaleFilter = source->iconMaleFilter;
	iconFemaleFilter = source->iconFemaleFilter;
	
	rareFirsts.RemoveAll();
	if ( source->rareFirsts.GetSize() > 0 ) {
		NiTListIterator i = source->rareFirsts.GetHeadPos();
		while (i) {
			rareFirsts.AddTail(source->rareFirsts.GetNext(i));
		}
	}
	
	rareSeconds.RemoveAll();
	if ( source->rareSeconds.GetSize() > 0 ) {
		NiTListIterator i = source->rareSeconds.GetHeadPos();
		while (i) {
			rareSeconds.AddTail(source->rareSeconds.GetNext(i));
		}
	}
}

bool AffixFilter::CompareTo(TESForm& compareTo)
{
    _VMESSAGE("Comparing '%s'/%p:%p @ &lt;%p&gt; TO '%s'/%p:%p @ &lt;%p&gt; ",
        GetEditorID(),GetFormType(),formID,this,compareTo.GetEditorID(),compareTo.GetFormType(),compareTo.formID,&compareTo);
    /*
        Return false if forms are identical, including polymorphic type.
        This method *MUST* be overwritten (TESForm::CompareTo does nothing).
        This function apparently *does* compare 'index' values like formID, editorID, mgefCode, etc.
    */

    AffixFilter* source = dynamic_cast<AffixFilter*>(&compareTo);
    if (!source) return true;    // source has wrong polymorphic type

    if (CompareAllComponentsTo(compareTo)) return true; // compare all BaseFormComponent properties
	if ( migs.GetSize() != source->migs.GetSize() ) return true;
	if ( minVal != source->minVal ) return true;
	if ( maxVal != source->maxVal ) return true;
	if ( minWht != source->minWht ) return true;
	if ( maxWht != source->maxWht ) return true;
	if ( app != source->app ) return true;
	if ( wep != source->wep ) return true;
	if ( app_types != source->app_types ) return true;
	if ( wep_types != source->wep_types ) return true;
	if ( minDur != source->minDur ) return true;
	if ( maxDur != source->maxDur ) return true;
	if ( nameFilter != source->nameFilter ) return true;

	if ( minDmg != source->minDmg ) return true;
	if ( maxDmg != source->maxDmg ) return true;
	if ( minRng != source->minRng ) return true;
	if ( maxRng != source->maxRng ) return true;
	if ( minSpd != source->minSpd ) return true;
	if ( maxSpd != source->maxSpd ) return true;
	if ( ignoresNWRes != source->ignoresNWRes ) return true;
	if ( weaponFilter != source->weaponFilter ) return true;
	if ( iconWeaponFilter != source->iconWeaponFilter ) return true;

	if ( hidesRings != source->hidesRings ) return true;
	if ( hidesAmulet != source->hidesAmulet ) return true;
	if ( head != source->head ) return true;
	if ( hair != source->hair ) return true;
	if ( bodyUp != source->bodyUp ) return true;
	if ( bodyLo != source->bodyLo ) return true;
	if ( hand != source->hand ) return true;
	if ( foot != source->foot ) return true;
	if ( ringR != source->ringR ) return true;
	if ( ringL != source->ringL ) return true;
	if ( amulet != source->amulet ) return true;
	if ( weapon != source->weapon ) return true;
	if ( weaponBack != source->weaponBack ) return true;
	if ( weaponSide != source->weaponSide ) return true;
	if ( quiver != source->quiver ) return true;
	if ( shield != source->shield ) return true;
	if ( torch != source->torch ) return true;
	if ( tail != source->tail ) return true;
	if ( bipedMaleFilter != source->bipedMaleFilter ) return true;
	if ( bipedFemaleFilter != source->bipedFemaleFilter ) return true;
	if ( groundMaleFilter != source->groundMaleFilter ) return true;
	if ( groundFemaleFilter != source->groundFemaleFilter ) return true;
	if ( iconMaleFilter != source->iconMaleFilter ) return true;
	if ( iconFemaleFilter != source->iconFemaleFilter ) return true;

	if ( migs.GetSize() != source->migs.GetSize() ) return true;
	if ( migs.GetSize() > 0 ) {
		NiTListIterator i = migs.GetHeadPos();
		NiTListIterator j = source->migs.GetHeadPos();
		Affixes* iPtr = migs.GetNext(i);
		Affixes* jPtr = source->migs.GetNext(j);
		while ( i && j ) {
			if ( iPtr->first != jPtr->first ) return true;
			if ( iPtr->second != jPtr->second ) return true;
			Affixes* iPtr = migs.GetNext(i);
			Affixes* jPtr = source->migs.GetNext(j);
		}
		if (i) return true;
		if (j) return true;
	}

	if ( rareFirsts.GetSize() != source->migs.GetSize() ) return true;
	if ( rareFirsts.GetSize() > 0 ) {
		NiTListIterator i = rareFirsts.GetHeadPos();
		NiTListIterator j = source->rareFirsts.GetHeadPos();
		BSStringT* iPtr = rareFirsts.GetNext(i);
		BSStringT* jPtr = rareFirsts.GetNext(i);
		while ( i && j ) {
			if ( *iPtr != *jPtr ) return true;
			iPtr = rareFirsts.GetNext(i);
			jPtr = rareFirsts.GetNext(i);
		}
		if (i) return true;
		if (j) return true;
	}

	if ( rareSeconds.GetSize() != source->migs.GetSize() ) return true;
	if ( rareSeconds.GetSize() > 0 ) {
		NiTListIterator i = rareSeconds.GetHeadPos();
		NiTListIterator j = source->rareSeconds.GetHeadPos();
		BSStringT* iPtr = rareSeconds.GetNext(i);
		BSStringT* jPtr = rareSeconds.GetNext(i);
		while ( i && j ) {
			if ( *iPtr != *jPtr ) return true;
			iPtr = rareSeconds.GetNext(i);
			jPtr = rareSeconds.GetNext(i);
		}
		if (i) return true;
		if (j) return true;
	}

	return false;
}

#ifndef OBLIVION
void AffixFilter::SetInDialog(HWND hwndDlg) {
	fltDlg->currPtr = currPtr = this;

	_VMESSAGE("Loading AffixFilter '%s' into dialog.", GetEditorID());
	TESForm::SetInDialog(hwndDlg);

	fltDlg->CEditorID.SetWindowText(GetEditorID());
	fltDlg->EDID = GetEditorID();

	for ( int i = 0; i < CAffixFilterDlg::pageCt; ++i ) {
		if ( !fltDlg->tabs[i]->Load(*this) ) {
			_ERROR("Tab #%i failed to load.", i);
		}
	}

	for ( UInt32 i = 1; i < fltDlg->CMigGrid.GetRowCount(); ++i ) {
		MagicItemGenerator* mig = (MagicItemGenerator*)fltDlg->CMigGrid.GetCell(i, 0)->GetData();
		if ( mig ) {
			UInt8 new_soul = 0;
			NiTListIterator k = migs.GetHeadPos();
			while ( k ) {
				Affixes* aff = migs.GetNext(k);
				if ( aff && aff->first == mig ) {
					new_soul = aff->second;
					break;
				}
			}
			for ( UInt32 j = 3; j < 11; ++j ) {
				UInt8 soul = 1 << (j-3);
				CGridCellCheck* cell = dynamic_cast<CGridCellCheck*>(fltDlg->CMigGrid.GetCell(i, j));
				if ( cell ) {
					if ( mig->souls & new_soul & soul ) {
						cell->SetCheck(TRUE);
						_DMESSAGE("Got MIG '%s' with soul %i (%X).", mig->GetEditorID(), j-3, soul);
					}
					else
						cell->SetCheck(FALSE);
				}
				else
					_ERROR("Cell (%i,%i) does not have a checkbox?", i, j);
			}
		}
		else
			_ERROR("Nulled MIG lParam at row %i.", i);
	}
	
	fltDlg->dlgMod(false, true);

	fltDlg->CEditorID.SetFocus();
}

void AffixFilter::GetFromDialog(HWND dialog) {
	_VMESSAGE("Retrieving AffixFilter '%s' from dialog.", GetEditorID());

	for ( int i = 0; i < fltDlg->pageCt; ++i ) {
		if ( !fltDlg->tabs[i]->Save(*this) ) {
			_ERROR("Tab #%i failed to save.", i);
		}
	}

	// handle affix grid
	migs.RemoveAll();
	for ( UInt32 i = 0, k = 0; i < fltDlg->CMigGrid.GetRowCount(); ++i ) {
		for ( UInt32 j = 3; j < 11; ++j ) {
			CGridCellCheck* cell = dynamic_cast<CGridCellCheck*>(fltDlg->CMigGrid.GetCell(i, j));
			if ( cell && cell->GetCheck() == BST_CHECKED ) {
				MagicItemGenerator* mig = (MagicItemGenerator*)cell->GetData();
				if ( mig ) {
					UInt8 soul = 1 << (j-3);
					if ( mig->souls & soul ) {
						if ( migs.GetSize() > 0 && migs.GetTail()->first == mig )
							migs.GetTail()->second |= soul;
						else
							migs.AddTail(new Affixes(mig, soul));
						_DMESSAGE("Added MIG '%s' with soul %i (%X).", mig->GetEditorID(), j-3, soul);
					}
					else
						_ERROR("Selected affix (%i) that MIG '%s' does not have?", j-3, mig->GetEditorID());
				}
				else
					_ERROR("Null cell lParam value.");
			}
		}
	}

	fltDlg->dlgMod(false);
}
#endif

// bitmask of virtual methods that need to be manually copied from vanilla vtbls (see notes in Constructor)
#ifdef OBLIVION
    const UInt32 TESForm_NoUseMethods[2] = { 0x86048400, 0x001E1FC7 };
#else
    const UInt32 TESForm_NoUseMethods[3] = { 0x090800D0, 0x0F783F8F, 0x00000105 };
#endif
extern memaddr TESForm_vtbl;

// Constructor
AffixFilter::AffixFilter()
	: TESForm()
	, migs(NiTOrphanList<Affixes>())
	, nameFilter("")
	, minVal(0), maxVal(-1)
	, minWht(0), maxWht(-1)
	, app(true), wep(false)
	, app_types(0), wep_types(0)
	, minDur(0), maxDur(-1)
	, hidesRings(2), hidesAmulet(2)
	, head(2), hair(2), bodyUp(2), bodyLo(2), hand(2), foot(2)
	, ringR(2), ringL(2), amulet(2), weapon(2), weaponBack(2), weaponSide(2), quiver(2), shield(2), torch(2), tail(2)
	, bipedMaleFilter(""), bipedFemaleFilter(""), groundMaleFilter(""), groundFemaleFilter(""), iconMaleFilter(""), iconFemaleFilter("")
	, minDmg(0), maxDmg(0), minRng(0), maxRng(-1), minSpd(0), maxSpd(-1), ignoresNWRes(2), weaponFilter(""), iconWeaponFilter("")
{
    /*
        Initialize a new instance of this form class
        Note the initializers following the function name above - these call the default contstructors
        for each of the base classes, and initialize the value of the extraData to zero.
    */

    // set the form type assigned during extended form registration
    formType = extendedForm.FormType();

    /*
            Many of the Oblivion classes defined in COEF are incomplete.  In particular, while
        the number of virtual methods is always correct, the signatures of many are still unknown.
        For compatibility reasons, COEF marks such methods with the '_NOUSE' macro and does not
        import them.  This means that when the compiler automatically generates a virtual method
        table (vtbl) for each class, some of the entries are blank.       
            When dealing with instances created by the Game/CS this makes little differece, since 
        those objects use the Game/CS version of the vtbls.  If an instance is created by a COEF-
        based plugin, however, it will use the COEF version of the vtbl.  This can lead to serious 
        errors.
            To address this, the COEF vtbl must be patched at run time by copying the addresses of
        the NOUSE_ methods from the vanilla vtbl to fill in the blanks.  It needs to be done only
        once per new form class, the first time that class' constructor is called.
    */
    if (static bool runonce = true)
    {
        // patch up TESForm vtbl    
        memaddr thisvtbl = (UInt32)memaddr::GetObjectVtbl(this);
        _MESSAGE("Patching AffixFilter TESForm vtbl @ &lt;%p&gt;",thisvtbl);
        gLog.Indent();
        for (int i = 0; i < sizeof(TESForm_NoUseMethods)*0x8; i++)
        {
            if ((TESForm_NoUseMethods[i/0x20] >> (i%0x20)) & 1)
            {
                thisvtbl.SetVtblEntry(i*4,TESForm_vtbl.GetVtblEntry(i*4));
                _VMESSAGE("Patched Offset 0x%04X",i*4);
            }
        }
        gLog.Outdent();
		_VMESSAGE("Done patching.");

        runonce  =  false;
    }
}

AffixFilter::AffixFilter(const AffixFilter& copied)
: TESForm(), migs(NiTOrphanList<Affixes>()), nameFilter(copied.nameFilter), minVal(copied.minVal), maxVal(copied.maxVal), minWht(copied.minWht), maxWht(copied.maxWht), app(copied.app), wep(copied.wep),\
app_types(copied.app_types), wep_types(copied.wep_types), minDur(copied.minDur), maxDur(copied.maxDur), hidesRings(copied.hidesRings), hidesAmulet(copied.hidesAmulet), head(copied.head), hair(copied.hair), bodyUp(copied.bodyUp), bodyLo(copied.bodyLo), hand(copied.hand), foot(copied.foot),\
ringR(copied.ringR), ringL(copied.ringL), amulet(copied.amulet), weapon(copied.weapon), weaponBack(copied.weaponBack), weaponSide(copied.weaponSide), quiver(copied.quiver), shield(copied.shield), torch(copied.torch), tail(copied.tail),\
bipedMaleFilter(copied.bipedMaleFilter), bipedFemaleFilter(copied.bipedFemaleFilter), groundMaleFilter(copied.groundMaleFilter), groundFemaleFilter(copied.groundFemaleFilter), iconMaleFilter(copied.iconMaleFilter), iconFemaleFilter(copied.iconFemaleFilter),\
minDmg(copied.minDmg), maxDmg(copied.maxDmg), minRng(copied.minRng), maxRng(copied.maxRng), minSpd(copied.minSpd), maxSpd(copied.maxSpd), ignoresNWRes(copied.ignoresNWRes), weaponFilter(copied.weaponFilter), iconWeaponFilter("") {
    _VMESSAGE("Constructing '%s'/%p:%p @ &lt;%p&gt; FROM %p @ &lt;%p&gt; ",
        GetEditorID(),GetFormType(),formID,this,copied.formID,&copied);
	NiTListIterator i = copied.migs.GetHeadPos();
	while (i) {
		migs.AddTail(copied.migs.GetNext(i));
	}
    formType = extendedForm.FormType(); 
}

// COEF ExtendedForm component
// This global object is used to register the form class with the ExtendedForm COEF component
ExtendedForm AffixFilter::extendedForm(SOLUTIONNAME,AFFIXFILTER_CLASSNAME,AFFIXFILTER_CLASSNAME,AffixFilter::CreateAffixFilter);  
TESForm* AffixFilter::CreateAffixFilter() { return new AffixFilter; } // method used by ExtendedForm to create new instances of this class

tri AffixFilter::intToCheck(const int& n) {
	if ( n < 0 )
		return BST_UNCHECKED;
	else if ( n > 0 )
		return BST_CHECKED;
	else
		return BST_INDETERMINATE;
}

int AffixFilter::checkToInt(const tri& n) {
	switch(n) {
		case BST_UNCHECKED:
			return -1;
		case BST_CHECKED:
			return 1;
		case BST_INDETERMINATE:
		default:
			return 0;
	}
}

UInt32 AffixFilter::GetNumAffixes() const {
	return migs.GetSize();
}

bool AffixFilter::AddAffix(MagicItemGenerator* new_mig, UInt8 new_soul) {
	if ( new_mig ) {
		migs.AddTail(new Affixes(new_mig, new_mig->souls & new_soul));
		return true;
	}
	return false;
}

bool AffixFilter::DeleteNthAffix(UInt32 n) {
	Affixes* aff = NULL;
	NiTListIterator k = NULL;
	if ( migs.NthItem(n, k, aff) ) {
		migs.Remove(aff);
		return true;
	}
	return false;
}

UInt32 AffixFilter::GetNthAffixMagicItemGenerator(UInt32 n) const {
	Affixes* aff = NULL;
	NiTListIterator k = NULL;
	if ( migs.NthItem(n, k, aff) )
		if ( aff )
			if ( aff->first )
				return aff->first->formID;
	return 0;
}

bool AffixFilter::SetNthAffixMagicItemGenerator(UInt32 n, MagicItemGenerator* new_mig) {
	Affixes* aff = NULL;
	NiTListIterator k = NULL;
	if ( migs.NthItem(n, k, aff) ) {
		if ( aff ) {
			aff->first = new_mig;
		}
	}
	return true;
}

UInt8 AffixFilter::GetNthAffixMask(UInt32 n) const {
	Affixes* aff = NULL;
	NiTListIterator k = NULL;
	if ( migs.NthItem(n, k, aff) )
		if ( aff )
			if ( aff->first )
				return aff->second;
	return 0;
}

bool AffixFilter::SetNthAffixMask(UInt32 n, UInt8 new_mask) {
	Affixes* aff = NULL;
	NiTListIterator k = NULL;
	if ( migs.NthItem(n, k, aff) ) {
		if ( aff ) {
			if ( aff->first ) {
				aff->second = new_mask;
				return true;
			}
		}
	}
	return false;
}

BSStringT AffixFilter::GetNameFilter() const {
	return nameFilter;
}

bool AffixFilter::SetNameFilter(const BSStringT& new_name) {
	nameFilter = new_name;
	return true;
}

double AffixFilter::GetMinValue() const {
	if ( numMask & kNum_MinVal )
		return minVal;
	else
		return -1;
}

bool AffixFilter::SetMinValue(double new_min_val) {
	if ( new_min_val >= 0 ) {
		minVal = new_min_val;
		numMask |= kNum_MinVal;
	}
	else
		numMask &= ~kNum_MinVal;
	return true;
}

double AffixFilter::GetMaxValue() const {
	if ( numMask & kNum_MaxVal )
		return maxVal;
	else
		return -1;
}

bool AffixFilter::SetMaxValue(double new_max_val) {
	if ( new_max_val >= 0 ) {
		maxVal = new_max_val;
		numMask |= kNum_MaxVal;
	}
	else
		numMask &= ~kNum_MaxVal;
	return true;
}

bool AffixFilter::CheckValue(double value) const {
	return !(((numMask & kNum_MinVal) && value < minVal) || ((numMask & kNum_MaxVal) && value > maxVal));
}

double AffixFilter::GetMinWeight() const {
	if ( numMask & kNum_MinWht )
		return minWht;
	else
		return -1;
}

bool AffixFilter::SetMinWeight(double new_min_wht) {
	if ( new_min_wht >= 0 ) {
		minWht = new_min_wht;
		numMask |= kNum_MinWht;
	}
	else
		numMask &= ~kNum_MinWht;
	return true;
}

double AffixFilter::GetMaxWeight() const {
	if ( numMask & kNum_MaxWht )
		return maxWht;
	else
		return -1;
}

bool AffixFilter::SetMaxWeight(double new_max_wht) {
	if ( new_max_wht >= 0 ) {
		maxWht = new_max_wht;
		numMask |= kNum_MaxWht;
	}
	else
		numMask &= ~kNum_MaxWht;
	return true;
}

bool AffixFilter::CheckWeight(double weight) const {
	return !(((numMask & kNum_MinWht) && weight < minWht) || ((numMask & kNum_MaxWht) && weight > maxWht));
}


double AffixFilter::GetMinDurability() const {
	if ( numMask & kNum_MinDur )
		return minDur;
	else
		return -1;
}

bool AffixFilter::SetMinDurability(double new_min_dur) {
	if ( new_min_dur >= 0 ) {
		minDur = new_min_dur;
		numMask |= kNum_MinDur;
	}
	else
		numMask &= ~kNum_MinDur;
	return true;
}

double AffixFilter::GetMaxDurability() const {
	if ( numMask & kNum_MaxDur )
		return maxDur;
	else
		return -1;
}

bool AffixFilter::SetMaxDurability(double new_max_dur) {
	if ( new_max_dur >= 0 ) {
		maxDur = new_max_dur;
		numMask |= kNum_MaxDur;
	}
	else
		numMask &= ~kNum_MaxDur;
	return true;
}

bool AffixFilter::CheckDurability(double durability) const {
	return !(((numMask & kNum_MinDur) && durability < minDur) || ((numMask & kNum_MaxDur) && durability > maxDur));
}

UInt8 AffixFilter::GetApparelMask() const {
	return app_types;
}

bool AffixFilter::SetApparelMask(UInt8 new_mask) {
	app_types = new_mask & kTypeApp_ALL;
	app = app_types != 0;
	arm = ( (app_types & kTypeApp_Light) != 0 || (app_types & kTypeApp_Heavy) != 0);
	return true;
}

double AffixFilter::GetMinArmorRating() const {
	if ( numMask & kNum_MinAR )
		return minAR;
	else
		return -1;
}

bool AffixFilter::SetMinArmorRating(double new_min_AR) {
	if ( new_min_AR >= 0 ) {
		minAR = new_min_AR;
		numMask |= kNum_MinAR;
	}
	else
		numMask &= ~kNum_MinAR;
	return true;
}

double AffixFilter::GetMaxArmorRating() const {
	if ( numMask & kNum_MaxAR )
		return maxAR;
	else
		return -1;
}

bool AffixFilter::SetMaxArmorRating(double new_max_AR) {
	if ( new_max_AR >= 0 ) {
		maxAR = new_max_AR;
		numMask |= kNum_MaxAR;
	}
	else
		numMask &= ~kNum_MaxAR;
	return true;
}

bool AffixFilter::CheckArmorRating(double AR) const {
	return !(((numMask & kNum_MinAR) && AR < minAR) || ((numMask & kNum_MaxAR) && AR > maxAR));
}

tri AffixFilter::GetHidesRings() const {
	return hidesRings;
}

bool AffixFilter::SetHidesRings(tri new_check) {
	hidesRings = new_check;
	return true;
}

tri AffixFilter::GetHidesAmulet() const {
	return hidesAmulet;
}

bool AffixFilter::SetHidesAmulet(tri new_check) {
	hidesAmulet = new_check;
	return true;
}

tri AffixFilter::GetBipedHead() const {
	return head;
}

bool AffixFilter::SetBipedHead(tri new_check) {
	head = new_check;
	return true;
}

tri AffixFilter::GetBipedHair() const {
	return hair;
}

bool AffixFilter::SetBipedHair(tri new_check) {
	hair = new_check;
	return true;
}

tri AffixFilter::GetBipedBodyLower() const {
	return bodyLo;
}

bool AffixFilter::SetBipedBodyLower(tri new_check) {
	bodyLo = new_check;
	return true;
}

tri AffixFilter::GetBipedBodyUpper() const {
	return bodyUp;
}

bool AffixFilter::SetBipedBodyUpper(tri new_check) {
	bodyUp = new_check;
	return true;
}

tri AffixFilter::GetBipedHand() const {
	return hand;
}

bool AffixFilter::SetBipedHand(tri new_check) {
	hand = new_check;
	return true;
}

tri AffixFilter::GetBipedFoot() const {
	return foot;
}

bool AffixFilter::SetBipedFoot(tri new_check) {
	foot = new_check;
	return true;
}

tri AffixFilter::GetBipedRingLeft() const {
	return ringL;
}

bool AffixFilter::SetBipedRingLeft(tri new_check) {
	ringL = new_check;
	return true;
}

tri AffixFilter::GetBipedRingRight() const {
	return ringR;
}

bool AffixFilter::SetBipedRingRight(tri new_check) {
	ringR = new_check;
	return true;
}

tri AffixFilter::GetBipedAmulet() const {
	return amulet;
}

bool AffixFilter::SetBipedAmulet(tri new_check) {
	amulet = new_check;
	return true;
}

tri AffixFilter::GetBipedWeapon() const {
	return weapon;
}

bool AffixFilter::SetBipedWeapon(tri new_check) {
	weapon = new_check;
	return true;
}

tri AffixFilter::GetBipedWeaponBack() const {
	return weaponBack;
}

bool AffixFilter::SetBipedWeaponBack(tri new_check) {
	weaponBack = new_check;
	return true;
}

tri AffixFilter::GetBipedWeaponSide() const {
	return weaponSide;
}

bool AffixFilter::SetBipedWeaponSide(tri new_check) {
	weaponSide = new_check;
	return true;
}

tri AffixFilter::GetBipedQuiver() const {
	return quiver;
}

bool AffixFilter::SetBipedQuiver(tri new_check) {
	quiver = new_check;
	return true;
}

tri AffixFilter::GetBipedShield() const {
	return shield;
}

bool AffixFilter::SetBipedShield(tri new_check) {
	shield = new_check;
	return true;
}

tri AffixFilter::GetBipedTorch() const {
	return torch;
}

bool AffixFilter::SetBipedTorch(tri new_check) {
	torch = new_check;
	return true;
}

tri AffixFilter::GetBipedTail() const {
	return tail;
}

bool AffixFilter::SetBipedTail(tri new_check) {
	tail = new_check;
	return true;
}
/*
int AffixFilter::GetHidesRings() const {
	return checkToInt(hidesRings);
}

bool AffixFilter::SetHidesRings(int new_check) {
	hidesRings = intToCheck(new_check);
	return true;
}

int AffixFilter::GetHidesAmulet() const {
	return checkToInt(hidesAmulet);
}

bool AffixFilter::SetHidesAmulet(int new_check) {
	hidesAmulet = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedHead() const {
	return checkToInt(head);
}

bool AffixFilter::SetBipedHead(int new_check) {
	head = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedHair() const {
	return checkToInt(hair);
}

bool AffixFilter::SetBipedHair(int new_check) {
	hair = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedBodyLower() const {
	return checkToInt(bodyLo);
}

bool AffixFilter::SetBipedBodyLower(int new_check) {
	bodyLo = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedBodyUpper() const {
	return checkToInt(bodyUp);
}

bool AffixFilter::SetBipedBodyUpper(int new_check) {
	bodyUp = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedHand() const {
	return checkToInt(hand);
}

bool AffixFilter::SetBipedHand(int new_check) {
	hand = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedFoot() const {
	return checkToInt(foot);
}

bool AffixFilter::SetBipedFoot(int new_check) {
	foot = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedRingLeft() const {
	return checkToInt(ringL);
}

bool AffixFilter::SetBipedRingLeft(int new_check) {
	ringL = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedRingRight() const {
	return checkToInt(ringR);
}

bool AffixFilter::SetBipedRingRight(int new_check) {
	ringR = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedAmulet() const {
	return checkToInt(amulet);
}

bool AffixFilter::SetBipedAmulet(int new_check) {
	amulet = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedWeapon() const {
	return checkToInt(weapon);
}

bool AffixFilter::SetBipedWeapon(int new_check) {
	weapon = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedWeaponBack() const {
	return checkToInt(weaponBack);
}

bool AffixFilter::SetBipedWeaponBack(int new_check) {
	weaponBack = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedWeaponSide() const {
	return checkToInt(weaponSide);
}

bool AffixFilter::SetBipedWeaponSide(int new_check) {
	weaponSide = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedQuiver() const {
	return checkToInt(quiver);
}

bool AffixFilter::SetBipedQuiver(int new_check) {
	quiver = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedShield() const {
	return checkToInt(shield);
}

bool AffixFilter::SetBipedShield(int new_check) {
	shield = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedTorch() const {
	return checkToInt(torch);
}

bool AffixFilter::SetBipedTorch(int new_check) {
	torch = intToCheck(new_check);
	return true;
}

int AffixFilter::GetBipedTail() const {
	return checkToInt(tail);
}

bool AffixFilter::SetBipedTail(int new_check) {
	tail = intToCheck(new_check);
	return true;
}
*/
BSStringT AffixFilter::GetModelFilterBipedMale() const {
	return bipedMaleFilter;
}

bool AffixFilter::SetModelFilterBipedMale(const BSStringT& new_name) {
	bipedMaleFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetModelFilterBipedFemale() const {
	return bipedFemaleFilter;
	
}

bool AffixFilter::SetModelFilterBipedFemale(const BSStringT& new_name) {
	bipedFemaleFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetModelFilterGroundMale() const {
	return groundMaleFilter;
}

bool AffixFilter::SetModelFilterGroundMale(const BSStringT& new_name) {
	groundMaleFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetModelFilterGroundFemale() const {
	return groundFemaleFilter;
}

bool AffixFilter::SetModelFilterGroundFemale(const BSStringT& new_name) {
	groundFemaleFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetIconFilterMale() const {
	return iconMaleFilter;
}

bool AffixFilter::SetIconFilterMale(const BSStringT& new_name) {
	iconMaleFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetIconFilterFemale() const {
	return iconFemaleFilter;
}

bool AffixFilter::SetIconFilterFemale(const BSStringT& new_name) {
	iconFemaleFilter = new_name;
	return true;
}


UInt8 AffixFilter::GetWeaponMask() const {
	return wep_types;
}

bool AffixFilter::SetWeaponMask(UInt8 new_mask) {
	wep_types = new_mask & kTypeWep_ALL;
	wep = wep_types!=0;
	return true;
}

double AffixFilter::GetMinDamage() const {
	if ( numMask & kNum_MinDmg )
		return minDmg;
	else
		return -1;
}

bool AffixFilter::SetMinDamage(double new_min_dmg) {
	if ( new_min_dmg >= 0 ) {
		minDmg = new_min_dmg;
		numMask |= kNum_MinDmg;
	}
	else
		numMask &= ~kNum_MinDmg;
	return true;
}

double AffixFilter::GetMaxDamage() const {
	if ( numMask & kNum_MaxDmg )
		return maxDmg;
	else
		return -1;
}

bool AffixFilter::SetMaxDamage(double new_max_dmg) {
	if ( new_max_dmg >= 0 ) {
		maxDmg = new_max_dmg;
		numMask |= kNum_MaxDmg;
	}
	else
		numMask &= ~kNum_MaxDmg;
	return true;
}

bool AffixFilter::CheckDamage(double damage) const {
	return !(((numMask & kNum_MinDmg) && damage < minDmg) || ((numMask & kNum_MaxDmg) && damage > maxDmg));
}

double AffixFilter::GetMinReach() const {
	if ( numMask & kNum_MinRng )
		return minRng;
	else
		return -1;
}

bool AffixFilter::SetMinReach(double new_min_rng) {
	if ( new_min_rng >= 0 ) {
		minRng = new_min_rng;
		numMask |= kNum_MinRng;
	}
	else
		numMask &= ~kNum_MinRng;
	return true;
}

double AffixFilter::GetMaxReach() const {
	if ( numMask & kNum_MaxRng )
		return maxRng;
	else
		return -1;
}

bool AffixFilter::CheckReach(double reach) const {
	return !(((numMask & kNum_MinRng) && reach < minRng) || ((numMask & kNum_MaxRng) && reach > maxRng));
}

bool AffixFilter::SetMaxReach(double new_max_rng) {
	if ( new_max_rng >= 0 ) {
		maxRng = new_max_rng;
		numMask |= kNum_MaxRng;
	}
	else
		numMask &= ~kNum_MaxRng;
	return true;
}

double AffixFilter::GetMinSpeed() const {
	if ( numMask & kNum_MinSpd )
		return minSpd;
	else
		return -1;
}

bool AffixFilter::SetMinSpeed(double new_min_spd) {
	if ( new_min_spd >= 0 ) {
		minSpd = new_min_spd;
		numMask |= kNum_MinSpd;
	}
	else
		numMask &= ~kNum_MinSpd;
	return true;
}

double AffixFilter::GetMaxSpeed() const {
	if ( numMask & kNum_MaxSpd )
		return maxSpd;
	else
		return -1;
}

bool AffixFilter::SetMaxSpeed(double new_max_spd) {
	if ( new_max_spd >= 0 ) {
		maxSpd = new_max_spd;
		numMask |= kNum_MaxSpd;
	}
	else
		numMask &= ~kNum_MaxSpd;
	return true;
}

bool AffixFilter::CheckSpeed(double speed) const {
	return !(((numMask & kNum_MinSpd) && speed < minSpd) || ((numMask & kNum_MaxSpd) && speed > maxSpd));
}

tri AffixFilter::GetIgnoresNormalWeaponResistance() const {
	return ignoresNWRes;
}

bool AffixFilter::SetIgnoresNormalWeaponResistance(tri new_check) {
	ignoresNWRes = new_check;
	return true;
}

BSStringT AffixFilter::GetModelFilterWeapon() const {
	return weaponFilter;
}

bool AffixFilter::SetModelFilterWeapon(const BSStringT& new_name) {
	weaponFilter = new_name;
	return true;
}

BSStringT AffixFilter::GetIconFilterWeapon() const {
	return iconWeaponFilter;
}

bool AffixFilter::SetIconFilterWeapon(const BSStringT& new_name) {
	iconWeaponFilter = new_name;
	return true;
}

UInt8 AffixFilter::GetNumRareFirstNames() const {
	return rareFirsts.GetSize();
}

bool AffixFilter::AddRareFirstName(const BSStringT& new_name) {
	if ( GetNumRareFirstNames() < 0xFF ) {
		rareFirsts.AddTail(new BSStringT(new_name));
		return true;
	}
	else
		return false;
}

BSStringT AffixFilter::GetNthRareFirstName(UInt8 i) const {
	if ( i < GetNumRareFirstNames() ) {
		UInt8 j = 0;
		NiTListIterator k = rareFirsts.GetHeadPos();
		BSStringT* l;
		for ( l = rareFirsts.GetNext(k); j < i; ++j, l = rareFirsts.GetNext(k) );
		if ( l )
			return *l;
	}
	return "";
}

bool AffixFilter::DeleteNthRareFirstName(UInt8 i) {
	if ( i < GetNumRareFirstNames() ) {
		UInt8 j = 0;
		NiTListIterator k = rareFirsts.GetHeadPos();
		BSStringT* l;
		for ( l = rareFirsts.GetNext(k); j < i; ++j, l = rareFirsts.GetNext(k) );
		rareFirsts.RemovePos(k);
		return true;
	}
	return false;
}

UInt8 AffixFilter::GetNumRareSecondNames() const {
	return rareSeconds.GetSize();
}

bool AffixFilter::AddRareSecondName(const BSStringT& new_name) {
	if ( GetNumRareSecondNames() < 0xFF ) {
		rareSeconds.AddTail(new BSStringT(new_name));
		return true;
	}
	else
		return false;
}

BSStringT AffixFilter::GetNthRareSecondName(UInt8 i) const {
	if ( i < GetNumRareSecondNames() ) {
		UInt8 j = 0;
		NiTListIterator k = rareSeconds.GetHeadPos();
		BSStringT* l;
		for ( l = rareSeconds.GetNext(k); j < i; ++j, l = rareSeconds.GetNext(k) );
		if ( l )
			return *l;
	}
	return "";
}

bool AffixFilter::DeleteNthRareSecondName(UInt8 i) {
	if ( i < GetNumRareSecondNames() ) {
		UInt8 j = 0;
		NiTListIterator k = rareSeconds.GetHeadPos();
		BSStringT* l;
		for ( l = rareSeconds.GetNext(k); j < i; ++j, l = rareSeconds.GetNext(k) );
		rareSeconds.RemovePos(k);
		return true;
	}
	return false;
}
#ifdef OBLIVION
#define checkItemNum(var, type, param, func, desc, spec) \
	if ( !Check ## func ## ( var ## -> ## param ) ) { \
		_DMESSAGE( #type " '%s' &lt;%p&gt; has " #desc " " #spec "; AffixFilter &lt;%p&gt; requires %.0f to %.0f.", var ## ->name.c_str(), var ## ->formID, var ## -> ## param, formID, GetMin ## func ## (), GetMax ## func ## ()); \
		return false; \
	} \
	else \
		(void*)0

#define checkBipSlot(maskName, triName, desc) \
	if ( (apparel->bipedSlotMask & TESBipedModelForm::kSlotMask_ ## maskName) != 0 ) { \
		if ( GetBiped ## triName ## () == BST_UNCHECKED ) { \
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel not use the " #desc " slot; this does.", formID); \
			return false; \
		} \
	} \
	else { \
		if ( GetBiped ## triName ## () == BST_CHECKED ) { \
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel use the " #desc " slot; this does not.", formID); \
			return false; \
		} \
	}

bool regex_search(const BSStringT& str, const BSStringT& exp) {
	if ( exp.Size() == 0 )
		return true;
	boost::regex e (exp.c_str());
	return boost::regex_search(str.c_str(), e);
}

bool regex_search(const char* str, const BSStringT& exp) {
	if ( exp.Size() == 0 )
		return true;
	boost::regex e (exp.c_str());
	return boost::regex_search(str, e);
}

bool AffixFilter::Check(const TESObjectARMO* armor) const {
	if ( !armor ) {
		_ERROR("AffixFilter &lt;%p&gt; received NULL.", formID);
		return false;
	}

	bool okLight = (GetApparelMask() & kTypeApp_Light) != 0;
	bool okHeavy = (GetApparelMask() & kTypeApp_Heavy) != 0;

	if ( GetApparelMask() == 0 ) {
		_DMESSAGE("AffixFilter &lt;%p&gt; does not affect armor.", formID);
		return false;
	}

	checkItemNum(armor, Armor, goldValue, Value, gold value, %i);
	checkItemNum(armor, Armor, weight, Weight, weight, %.0f);
	checkItemNum(armor, Armor, health, Durability, durability, %i);
	checkItemNum(armor, Armor, armorRating, ArmorRating, AR, %i);

	if ( const_cast<TESObjectARMO*>(armor)->IsHeavyArmor() ) {
		if ( !okHeavy ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; does not affect Heavy Armor like '%s' &lt;%p&gt;.", formID, armor->name.c_str(), armor->formID);
			return false;
		}
	}
	else {
		if ( !okLight ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; does not affect Light Armor like '%s' &lt;%p&gt;.", formID, armor->name.c_str(), armor->formID);
			return false;
		}
	}
	
	if ( !regex_search(armor->name, GetNameFilter()) )
		return false;

	return CheckApparel(armor);
}

bool AffixFilter::Check(const TESObjectCLOT* clothing) const {
	if ( !clothing ) {
		_DMESSAGE("AffixFilter &lt;%p&gt; received NULL.", formID);
		return false;
	}

	bool okCloth = (GetApparelMask() & kTypeApp_Cloth) != 0;

	if ( !okCloth ) {
		_DMESSAGE("AffixFilter &lt;%p&gt; does not affect clothing.", formID);
		return false;
	}

	checkItemNum(clothing, Clothing, goldValue, Value, gold value, %i);
	checkItemNum(clothing, Clothing, weight, Weight, weight, %.0f);
	
	if ( !regex_search(clothing->name, GetNameFilter()) )
		return false;

	return CheckApparel(clothing);
}

bool AffixFilter::CheckApparel(const TESBipedModelForm* apparel) const {
	if ( (apparel->bipedModelFlags & TESBipedModelForm::kBipedModelFlag_HidesAmulets) != 0 ) {
		if ( GetHidesAmulet() == BST_UNCHECKED ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel not hide amulets; this does.", formID);
			return false;
		}
	}
	else {
		if ( GetHidesAmulet() == BST_CHECKED ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel hide amulets; this does not.", formID);
			return false;
		}
	}

	if ( (apparel->bipedModelFlags & TESBipedModelForm::kBipedModelFlag_HidesRings) != 0 ) {
		if ( GetHidesRings() == BST_UNCHECKED ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel not hide rings; this does.", formID);
			return false;
		}
	}
	else {
		if ( GetHidesRings() == BST_CHECKED ) {
			_DMESSAGE("AffixFilter &lt;%p&gt; requires that apparel hide rings; armor this does not.", formID);
			return false;
		}
	}

	/*check Biped Slots*/
	checkBipSlot(Head, Head, Head)
	checkBipSlot(Hair, Hair, Hair)
	checkBipSlot(UpperBody, BodyUpper, Upper Body)
	checkBipSlot(LowerBody, BodyLower, Lower Body)
	checkBipSlot(Hand, Hand, Hand)
	checkBipSlot(Foot, Foot, Foot)
	checkBipSlot(LeftRing, RingLeft, Left Ring)
	checkBipSlot(RightRing, RingRight, Right Ring)
	checkBipSlot(Amulet, Amulet, Amulet)
	checkBipSlot(Weapon, Weapon, Weapon)
	checkBipSlot(BackWeapon, WeaponBack, Back Weapon)
	checkBipSlot(SideWeapon, WeaponSide, Side Weapon)
	checkBipSlot(Quiver, Quiver, Quiver)
	checkBipSlot(Shield, Shield, Shield)
	checkBipSlot(Torch, Torch, Torch)
	checkBipSlot(Tail, Tail, Tail)

	/*check RegEx filters*/
	if ( !regex_search(apparel->bipedModel[TESBipedModelForm::kGender_Male].modelPath, GetModelFilterBipedMale()) )
		return false;
	if ( !regex_search(apparel->worldModel[TESBipedModelForm::kGender_Male].modelPath, GetModelFilterGroundMale()) )
		return false;
	if ( !regex_search(apparel->bipedIcon[TESBipedModelForm::kGender_Male].texturePath, GetIconFilterMale()) )
		return false;
	
	if ( !regex_search(apparel->bipedModel[TESBipedModelForm::kGender_Female].modelPath, GetModelFilterBipedFemale()) )
		return false;
	if ( !regex_search(apparel->worldModel[TESBipedModelForm::kGender_Female].modelPath, GetModelFilterGroundFemale()) )
		return false;
	if ( !regex_search(apparel->bipedIcon[TESBipedModelForm::kGender_Female].texturePath, GetIconFilterFemale()) )
		return false;

	return true;
}

bool AffixFilter::Check(const TESObjectWEAP* weapon) const {
	if ( !weapon ) {
		_DMESSAGE("AffixFilter &lt;%p&gt; received NULL.", formID);
		return false;
	}

	if ( GetWeaponMask() == 0 ) {
		_DMESSAGE("AffixFilter &lt;%p&gt; does not affect weapons.", formID);
		return false;
	}

	checkItemNum(weapon, Weapon, goldValue, Value, gold value, %i);
	checkItemNum(weapon, Weapon, weight, Weight, weight, %.0f);
	checkItemNum(weapon, Weapon, health, Durability, durability, %i);
	checkItemNum(weapon, Weapon, damage, Damage, damage, %i);
	checkItemNum(weapon, Weapon, weaponReach, Reach, reach, %.2f);
	checkItemNum(weapon, Weapon, weaponSpeed, Speed, speed, %.2f);

	switch (weapon->weaponType) {
		case TESObjectWEAP::kWeaponType_BladeOneHand:
			if ( (GetWeaponMask() & kTypeWep_Blade1h) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect one-handed blades.", formID);
				return false;
			}
			break;

		case TESObjectWEAP::kWeaponType_BladeTwoHand:
			if ( (GetWeaponMask() & kTypeWep_Blade2h) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect two-handed blades.", formID);
				return false;
			}
			break;

		case TESObjectWEAP::kWeaponType_BluntOneHand:
			if ( (GetWeaponMask() & kTypeWep_Blunt1h) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect one-handed blunt weapons.", formID);
				return false;
			}
			break;

		case TESObjectWEAP::kWeaponType_BluntTwoHand:
			if ( (GetWeaponMask() & kTypeWep_Blunt2h) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect two-handed blunt weapons.", formID);
				return false;
			}
			break;

		case TESObjectWEAP::kWeaponType_Bow:
			if ( (GetWeaponMask() & kTypeWep_Bow) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect bows.", formID);
				return false;
			}
			break;

		case TESObjectWEAP::kWeaponType_Staff:
			if ( (GetWeaponMask() & kTypeWep_Staff) == 0 ) {
				_DMESSAGE("AffixFilter &lt;%p&gt; does not affect staves.", formID);
				return false;
			}
			break;
			
		default:
			_ERROR("Weapon '%s' &lt;%p&gt; does not have a valid type? Type given as %i.", weapon->name.c_str(), weapon->formID, weapon->weaponType);
			return false;
	}
	
	if ( !regex_search(weapon->name, GetNameFilter()) )
		return false;
	if ( !regex_search(weapon->modelPath, GetModelFilterWeapon()) )
		return false;
	if ( !regex_search(weapon->texturePath, GetIconFilterWeapon()) )
		return false;

	return true;
}

bool AffixFilter::SortAffixes(AffixList& prefixes, AffixList& suffixes, UInt16 level, bool levelLtEq /*=true*/) const {
	NiTListIterator i = migs.GetHeadPos();
	AffixList* affixesPtr;
	Affixes* aff = migs.GetHead();
	while (i) {
		aff = migs.GetNext(i);
		affixesPtr = ( aff->first->suf ? &suffixes : &prefixes );
		for ( UInt8 i = 0, j = 1; i < Souls::kSoulIndex_MAX; j = 1 << ++i ) {
			if ( aff->first ) {
				if ( (aff->second & j) != 0 && (aff->first->souls & j) != 0 ) {
					UInt16 lv = kSouls->getLv(i) * ( 1 + (aff->first->rank/100));
					if ( lv == level || ( lv < level && levelLtEq ) ) {
						affixesPtr->add(Affix(aff->first, i, 1 + (aff->first->freq/100)), lv);
					}
				}
			}
			else {
				_ERROR("AffixFilter &lt;%p&gt; has NULL MagicItemGenerator at #%i.", formID);
			}
		}
	}

	return true;
}

#else //#ifndef OBLIVION
// CS dialog management 
bool AffixFilter::mod = false;
AffixFilter*		AffixFilter::currPtr = NULL;
CAffixFilterDlg*	AffixFilter::fltDlg = NULL;
UInt32 AffixFilter::kMenuIdentifier = 0xCC00;    // unique identifier for new menu item (see InitializeMyForm())
HWND  AffixFilter::dialogHandle = 0; // handle of open dialog window, if any

void AffixFilter_AddMenuItem()
{
	// insert new item into CS main menu
	HMENU menu = GetMenu(TESDialog::csHandle); // get main CS menu handle
	menu = GetSubMenu(menu,5);              // get 'Gameplay' submenu handle
	MENUITEMINFO iteminfo;      
	iteminfo.cbSize = sizeof(iteminfo);        
	iteminfo.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
	iteminfo.fType = MFT_STRING;
	char menulabel[] = "Affix Filters...";
	iteminfo.dwTypeData = menulabel;
	iteminfo.cch = sizeof(menulabel);
	iteminfo.wID = AffixFilter::kMenuIdentifier;
	InsertMenuItem(menu,2,true,&iteminfo); // Insert new entry at the second location of submenu
}

LRESULT AffixFilter_CSMenuHook(WPARAM wparam, LPARAM lparam)
{
	/*
		CSMainWindow_WMCommand event handler
		Peeks at WM_COMMAND messages sent to the main CS window
		Returns zero if message has been handled
	*/
	if (LOWORD(wparam) == AffixFilter::kMenuIdentifier)
	{
		// WM_COMMAND messagesent by new menu item
		AffixFilter::OpenDialog();
		return 0;
	}
	return 1;
}

void AffixFilter::OpenDialog()
{
	/*
		Open CS dialog for editing forms of this type
	*/
	if ( !fltDlg ) { // don't create a new one if we already have one
		fltDlg = new CAffixFilterDlg;
		if ( fltDlg ) {
			if ( fltDlg->Create(IDD_FLTDLG, CWnd::FromHandle(dialogHandle)) )
				fltDlg->ShowWindow(SW_SHOW);
			else
				_ERROR("AffixFilterDlg::Create() failed.");
		}
		else
			_ERROR("new AffixFilterDlg failed.");
	}
	else {
		_VMESSAGE("AffixFilterDlg already open.");
		fltDlg->BringWindowToTop();
	}
}
#endif

// global initialization function
void AffixFilter::InitializeAffixFilter()
{
	/*
		Perform one-time initialization required for this form class
	*/
	_MESSAGE("Initializing " AFFIXFILTER_CLASSNAME " ...");

	// register form type with the ExtendedForm COEF component
	extendedForm.Register(AFFIXFILTER_SHORTNAME);
    
#ifndef OBLIVION

	// generate a (hopefully) unqiue menu identifier from form type
	kMenuIdentifier += extendedForm.FormType();

    // attempt to add new menu item to the CS
    // this may fail for newer (> v21) versions of OBSE that load plugins before 
    // the CS main window has been initialized.
    AffixFilter_AddMenuItem();

    // register CSWindows::InitializeWindows event handler with the EventManager COEF component
    // this event occurs during startup, after the CS has initialized the main MDI window and menu
    // items cannot be added to the main CS menu until this event has occurred
    // older versions of OBSE (<= v20) load plugins *after* this event, meaning it will never be trapped
    EventManager::CSWindows::InitializeWindows.RegisterCallback(&AffixFilter_AddMenuItem);

    // register CSWindows::MainW_WMCommand event handler with the EventManager COEF component
    // this event occurs when WM_COMMAND messages are sent to the main CS window
    // This includes menu selections by the user, so this event handler will be able to
    // determine when the user clicks on the newly added menu item.
	EventManager::CSWindows::MainW_WMCommand.RegisterCallback(&AffixFilter_CSMenuHook);

#endif
}

/*
UInt8 AffixFilter::app_codes[] = {
	IDC_STATIC7, IDC_STATIC8, IDC_STATIC9, IDC_STATIC10, IDC_STATIC11, IDC_STATIC12, IDC_STATIC13, IDC_STATIC14, IDC_STATIC15, IDC_STATIC16, IDC_STATIC17, IDC_STATIC18,\
	IDC_APP_TYPES, IDC_HIDE_RINGS, IDC_HIDE_AMULET, IDC_AR_MIN, IDC_AR_MAX,\
	IDC_HEAD, IDC_HAIR, IDC_BODY_UP, IDC_BODY_LO, IDC_HAND, IDC_FOOT,\
	IDC_RING_RIGHT, IDC_RING_LEFT, IDC_AMULET,\
	IDC_WEAPON, IDC_WEAPON_BACK, IDC_WEAPON_SIDE, IDC_QUIVER,\
	IDC_SHIELD, IDC_TORCH, IDC_TAIL,\
	IDC_MODEL_FILTER_BIPED_MALE, IDC_MODEL_FILTER_BIPED_FEMALE,\
	IDC_MODEL_FILTER_GROUND_MALE, IDC_MODEL_FILTER_GROUND_FEMALE,\
	IDC_ICON_FILTER_APP_MALE, IDC_ICON_FILTER_APP_FEMALE
};

UInt8 AffixFilter::wep_codes[] = {
	IDC_WEP_TYPES,\
	WEP_STATIC_DMG, WEP_STATIC_DMG_MIN, WEP_STATIC_DMG_MAX,\
	IDC_DMG_MIN, IDC_DMG_MAX,\
	WEP_STATIC_RNG, WEP_STATIC_RNG_MIN, WEP_STATIC_RNG_MAX,\
	IDC_RNG_MIN, IDC_RNG_MAX,\
	WEP_STATIC_SPD, WEP_STATIC_SPD_MIN, WEP_STATIC_SPD_MAX,\
	IDC_SPD_MIN, IDC_SPD_MAX,\
	IDC_IGNORES_NW_RES,\
	WEP_STATIC_MODEL, IDC_MODEL_FILTER_WEP,\
	WEP_STATIC_ICON, IDC_ICON_FILTER_WEP
};

bool AffixFilter::isWep() const {
	return is_wep;
}

void AffixFilter::isWep(bool new_is_wep, bool force ) {
#ifndef OBLIVION
	DialogSetType(new_is_wep, force, is_wep);
#endif
	is_wep = new_is_wep;
}

void AffixFilter::DialogSetType(bool nu, bool force, bool old) {
	if ( ( nu != old || force ) && kMenuIdentifier ) {
		SendDlgItemMessage(dialogHandle, IDC_APP, BM_SETCHECK, (nu?BST_UNCHECKED:BST_CHECKED), 0);
		for ( UInt8 i = 0; i < sizeof(app_codes); ++i )
			ShowWindow(GetDlgItem(dialogHandle, app_codes[i]), (nu?SW_HIDE:SW_NORMAL));

		SendDlgItemMessage(dialogHandle, IDC_WEP, BM_SETCHECK, (nu?BST_CHECKED:BST_UNCHECKED), 0);
		for ( UInt8 i = 0; i < sizeof(wep_codes); ++i )
			ShowWindow(GetDlgItem(dialogHandle, wep_codes[i]), (nu?SW_NORMAL:SW_HIDE));
	}
}

const UInt16 AffixFilter::text_codes[] = {\
	IDC_FLT_EDITORID, IDC_NAME_FILTER, \
	IDC_VAL_MIN, IDC_VAL_MAX, IDC_WHT_MIN, IDC_WHT_MAX, IDC_DUR_MIN, IDC_DUR_MAX, \
	IDC_AR_MIN, IDC_AR_MAX, \
	IDC_DMG_MIN, IDC_DMG_MAX, IDC_RNG_MIN, IDC_RNG_MAX, IDC_SPD_MIN, IDC_SPD_MAX, \
	IDC_MODEL_FILTER_BIPED_MALE, IDC_MODEL_FILTER_BIPED_FEMALE, \
	IDC_MODEL_FILTER_GROUND_MALE, IDC_MODEL_FILTER_GROUND_FEMALE, \
	IDC_ICON_FILTER_APP_MALE, IDC_ICON_FILTER_APP_FEMALE, \
	IDC_MODEL_FILTER_WEP, IDC_ICON_FILTER_WEP \
};

const UInt16 AffixFilter::bool_codes[] = {\
	IDC_APP, IDC_WEP \
};

const UInt16 AffixFilter::tri_codes[] = {\
	IDC_HIDE_RINGS, IDC_HIDE_AMULET, \
	IDC_HEAD, IDC_HAIR, IDC_BODY_UP, IDC_BODY_LO, IDC_HAND, IDC_FOOT, \
	IDC_RING_RIGHT, IDC_RING_LEFT, IDC_AMULET, \
	IDC_WEAPON, IDC_WEAPON_BACK, IDC_WEAPON_SIDE, IDC_QUIVER, \
	IDC_SHIELD, IDC_TORCH, IDC_TAIL, \
	IDC_IGNORES_NW_RES \
};
*/
